// 版权所有2009 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 包gzip实现了对gzip格式压缩文件的读写，
// 如RFC 1952所述。
package gzip

import (
	"bufio"
	"compress/flate"
	"encoding/binary"
	"errors"
	"hash/crc32"
	"io"
	"time"
)

const (
	gzipID1     = 0x1f
	gzipID2     = 0x8b
	gzipDeflate = 8
	flagText    = 1 << 0
	flagHdrCrc  = 1 << 1
	flagExtra   = 1 << 2
	flagName    = 1 << 3
	flagComment = 1 << 4
)

var (
	// 读取具有无效校验和的GZIP数据时返回ErrChecksum。
	ErrChecksum = errors.New("gzip: invalid checksum")
	// 读取头无效的GZIP数据时返回ErrHeader。
	ErrHeader = errors.New("gzip: invalid header")
)

var le = binary.LittleEndian

// 。EOF到io。出人意料的错误。
func noEOF(err error) error {
	if err == io.EOF {
		return io.ErrUnexpectedEOF
	}
	return err
}

// gzip文件存储一个头，该头给出了压缩文件的元数据。
// 该头作为Writer和Reader结构的字段公开。由于GZIP文件格式的限制，
// 
// 字符串必须是UTF-8编码的，并且只能包含Unicode代码点
// U+0001到U+00FF。
type Header struct {
	Comment string    // 注释
	Extra   []byte    // “额外数据”
	ModTime time.Time // 修改时间
	Name    string    // 文件名
	OS      byte      // 操作系统类型
}

// 读卡器是一个io。可以从gzip格式的压缩文件中读取
// 未压缩数据的读取器。
// 
// 一般来说，gzip文件可以是gzip文件的串联，每个文件都有自己的头。从读取器读取
// 返回每个读取器的未压缩数据的串联。
// 读卡器字段中只记录第一个标题。
// 
// Gzip文件存储未压缩数据的长度和校验和。
// 如果读到的
// 没有达到预期的长度或校验和，读卡器将在读到未压缩数据的末尾时返回一个错误校验和。在收到io之前，客户应将Read返回的数据
// 视为暂定数据。EOF 
// 标记数据的结束。
type Reader struct {
	Header       // 在NewReader或Reader之后有效。重置
	r            flate.Reader
	decompressor io.ReadCloser
	digest       uint32 // CRC-32，IEEE多项式（第8节）
	size         uint32 // 未压缩大小（第2.3.1节）
	buf          [512]byte
	err          error
	multistream  bool
}

// NewReader创建一个读取给定读卡器的新读卡器。
// 如果r不实现io。ByteReader，
// 解压器可能会从r读取超过必要的数据。
// 
// 完成后，调用方有责任关闭读卡器。
// 
// 读者。标头字段在返回的读取器中有效。
func NewReader(r io.Reader) (*Reader, error) {
	z := new(Reader)
	if err := z.Reset(r); err != nil {
		return nil, err
	}
	return z, nil
}

// 重置放弃读卡器z的状态，并使其等同于从NewReader读取其原始状态的结果，但改为从r读取。
// 这允许重用读卡器，而不是分配新的读卡器。
func (z *Reader) Reset(r io.Reader) error {
	*z = Reader{
		decompressor: z.decompressor,
		multistream:  true,
	}
	if rr, ok := r.(flate.Reader); ok {
		z.r = rr
	} else {
		z.r = bufio.NewReader(r)
	}
	z.Header, z.err = z.readHeader()
	return z.err
}

// 多流控制读卡器是否支持多流文件。
// 
// 如果启用（默认设置），读卡器希望输入是一个序列
// 单独压缩的数据流，每个数据流都有自己的头和尾，以EOF结尾。其效果是，gzip文件的序列
// 的串联被视为等同于序列的串联
// 的gzip。这是gzip阅读器的标准行为。
// 
// 调用多流（false）将禁用此行为；在读取区分单个gzip 
// 可能很有用。
// 数据流或将gzip数据流与其他数据流混合的文件格式时，禁用行为
// 在这种模式下，当读卡器到达数据流的末尾时，
// Read返回io。EOF。底层读卡器必须实现io。ByteReader 
// 以便在gzip流之后保留位置。
// 要启动下一个流，请先调用z.Reset（r），然后调用z.Multistream（false）。
// 如果没有下一个流，z.Reset（r）将返回io。EOF。
func (z *Reader) Multistream(ok bool) {
	z.multistream = ok
}

// readString从z.r读取以NUL结尾的字符串。
// 它将读取的字节视为编码为ISO 8859-1（拉丁语-1），
// 将输出使用UTF-8编码的字符串。
// 此方法始终使用读取的数据更新z.digest。
func (z *Reader) readString() (string, error) {
	var err error
	needConv := false
	for i := 0; ; i++ {
		if i >= len(z.buf) {
			return "", ErrHeader
		}
		z.buf[i], err = z.r.ReadByte()
		if err != nil {
			return "", err
		}
		if z.buf[i] > 0x7f {
			needConv = true
		}
		if z.buf[i] == 0 {
			// 摘要涵盖了NUL终止符。
			z.digest = crc32.Update(z.digest, crc32.IEEETable, z.buf[:i+1])

			// 字符串是ISO 8859-1，拉丁文-1（RFC 1952，第2.3.1节）。
			if needConv {
				s := make([]rune, 0, i)
				for _, v := range z.buf[:i] {
					s = append(s, rune(v))
				}
				return string(s), nil
			}
			return string(z.buf[:i]), nil
		}
	}
}

// readHeader根据第2.3.1节读取GZIP头。
// 此方法不设置z.err。
func (z *Reader) readHeader() (hdr Header, err error) {
	if _, err = io.ReadFull(z.r, z.buf[:10]); err != nil {
		// RFC 1952第2.2节规定：
		// gzip文件由一系列“成员”（压缩数据集）组成。
		// 
		// 除此之外，本规范未明确
		// /“系列”定义为“一个或多个”或“零个或多个”。要在警告方面犯错误，Go将其解释为“零或更多”。
		// 因此，可以返回io。这里是EOF。
		return hdr, err
	}
	if z.buf[0] != gzipID1 || z.buf[1] != gzipID2 || z.buf[2] != gzipDeflate {
		return hdr, ErrHeader
	}
	flg := z.buf[3]
	if t := int64(le.Uint32(z.buf[4:8])); t > 0 {
		// 第2.3.1节，MTIME的零值表示未设置
		// 修改时间。
		hdr.ModTime = time.Unix(t, 0)
	}
	// z.buf[8]是XFL，目前被忽略。
	hdr.OS = z.buf[9]
	z.digest = crc32.ChecksumIEEE(z.buf[:10])

	if flg&flagExtra != 0 {
		if _, err = io.ReadFull(z.r, z.buf[:2]); err != nil {
			return hdr, noEOF(err)
		}
		z.digest = crc32.Update(z.digest, crc32.IEEETable, z.buf[:2])
		data := make([]byte, le.Uint16(z.buf[:2]))
		if _, err = io.ReadFull(z.r, data); err != nil {
			return hdr, noEOF(err)
		}
		z.digest = crc32.Update(z.digest, crc32.IEEETable, data)
		hdr.Extra = data
	}

	var s string
	if flg&flagName != 0 {
		if s, err = z.readString(); err != nil {
			return hdr, err
		}
		hdr.Name = s
	}

	if flg&flagComment != 0 {
		if s, err = z.readString(); err != nil {
			return hdr, err
		}
		hdr.Comment = s
	}

	if flg&flagHdrCrc != 0 {
		if _, err = io.ReadFull(z.r, z.buf[:2]); err != nil {
			return hdr, noEOF(err)
		}
		digest := le.Uint16(z.buf[:2])
		if digest != uint16(z.digest) {
			return hdr, ErrHeader
		}
	}

	z.digest = 0
	if z.decompressor == nil {
		z.decompressor = flate.NewReader(z.r)
	} else {
		z.decompressor.(flate.Resetter).Reset(z.r, nil)
	}
	return hdr, nil
}

// 读取执行io。读卡器，从底层读卡器读取未压缩的字节。
func (z *Reader) Read(p []byte) (n int, err error) {
	if z.err != nil {
		return 0, z.err
	}

	n, z.err = z.decompressor.Read(p)
	z.digest = crc32.Update(z.digest, crc32.IEEETable, p[:n])
	z.size += uint32(n)
	if z.err != io.EOF {
		// 在正常情况下，我们返回这里。
		return n, z.err
	}

	// 完成文件；检查校验和和大小。
	if _, err := io.ReadFull(z.r, z.buf[:8]); err != nil {
		z.err = noEOF(err)
		return n, z.err
	}
	digest := le.Uint32(z.buf[:4])
	size := le.Uint32(z.buf[4:8])
	if digest != z.digest || size != z.size {
		z.err = ErrChecksum
		return n, z.err
	}
	z.digest, z.size = 0, 0

	// 文件正常；检查是否还有其他问题。
	if !z.multistream {
		return n, io.EOF
	}
	z.err = nil // 删除io。EOF 

	if _, z.err = z.readHeader(); z.err != nil {
		return n, z.err
	}

	// 如有必要，从下一个文件读取。
	if n > 0 {
		return n, nil
	}
	return z.Read(p)
}

// 关闭读卡器。它不会关闭基础io。读者
// 为了验证GZIP校验和，读卡器必须在io之前被完全消耗。EOF。
func (z *Reader) Close() error { return z.decompressor.Close() }
